<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>yqDiagram</title>
  <uieip type="file" value="inc" src="/yqwin/easyui/include.txt" cache="true" minute="1440"></uieip>
  <uieip type="file" value="css" src="/yqwin/css/yqwin.min" cache="false"></uieip>
  <uieip type="file" value="js" src="/yqwin/data/yqwin.min" cache="false"></uieip>
  <script src="/scripts/gojs/go.js"></script>

<body>
  <div id="div_desk">
  </div>
  <textarea id="txt_data" style="width: 100%;height: 35%;">
  </textarea>
  <a href="javascript:getValue();">getValue</a> |
  <a href="javascript:setValue();">setValue</a> |
  <a href="javascript:isEdit(true);">enabled</a> |
  <a href="javascript:isEdit(false);">disable</a> |
  <a href="javascript:destroy();">destroy</a> |
  <a href="javascript:init_ui();">init</a>
  <script>
    $(function () {
      init_ui();
    });

    function onresize() {
      var w = $(window).width() - 20;
      var h = $(window).height() * 0.6;
      $("#div_desk").yqDiagram("resize", {
        width: w,
        height: h,

      });

      $("#txt_data").css({
        "width": parseInt(w) + "px",
        "height": parseInt($(window).height() * 0.4 - 50) + "px"
      });
    }

    function init_ui() {
      $("#div_desk").yqDiagram({
        width: $(window).width(),
        height: $(window).height() * 0.6,
        isEdit: true,
        value: {
          "node": [
            {
              "category": "menu",
              "text": "系统管理",
              "icon": "/images/menu.png",
              "url": "http://www.uieip.com/",
              "key": -1,
              "loc": "-184 -78",
              "size": "180 155"
            },
            {
              "category": "menu",
              "key": "4shaWbVcoGO",
              "text": "系统管理",
              "icon": "/images/menu.png",
              "url": "http://www.uieip.com/",
              "loc": "278 7",
              "size": "80 60"
            },
            {
              "category": "label",
              "key": "4shaWy6adnQ",
              "type": "Rectangle",
              "text": "系统管理",
              "fontSize": 16,
              "loc": "-120 183",
              "size": "237 102"
            }
          ],
          "link": [
            {
              "from": -1,
              "to": "4shaWbVcoGO"
            },
            {
              "from": -1,
              "to": "4shaWy6adnQ"
            }
          ]
        },
        afterBindingTitle: function () {
          $(window).resize(function () {
            onresize();
          });
          onresize();
        },   //标题绑定后
      });
    }

    function getValue() {
      var v = $("#div_desk").yqDiagram("getValue");
      $("#txt_data").val(JSON.stringify(v, null, 2));
    }

    function setValue() {
      var v = $("#txt_data").val();
      $("#div_desk").yqDiagram("setValue", v);
    }

    function isEdit(tf) {
      $("#div_desk").yqDiagram("isEdit", tf);
    }

    function destroy() {
      $("#div_desk").yqDiagram("destroy");
    }
  </script>
  <script>
    /*diagram 图表编辑
      begin 2024/4/18
    */
    (function ($) {
      $.fn.yqDiagram = function (opts, param) {
        if (typeof opts == 'string') {
          return $.fn.yqDiagram.methods[opts](this, param);
        }

        var $this = this;
        var opt;
        if ($this.data("yqtype") == "yqDiagram") {
          destroy($this);
        }

        opt = $.extend({}, $.fn.yqDiagram.defaults, opts);
        var $uniqueID = $this.attr("ID");
        if ($uniqueID === undefined || $uniqueID === null || $uniqueID == "") {
          $uniqueID = $.getUniqueID();
          $this.attr("ID", $uniqueID);
        }

        if (opt.width === undefined && $this.width() > 0) {
          opt.width = $this.width();
        }
        else if (typeof opt.width != "undefined") {
          $this.width(opt.width);
        }

        if (opt.height === undefined && $this.height() > 0) {
          opt.height = $this.height();
        }
        else if (typeof opt.height != "undefined") {
          $this.height(opt.height);
        }

        opt._flowstate = {};

        //变量信息
        $this.data("dagram_opt", opt);
        $this.data("yqtype", "yqDiagram");

        init_menu($this)
      };

      $.fn.yqDiagram.defaults = {
        value: {
          "node": [
            {
              "category": "menu",
              "key": "4shaWbVcoGO",
              "stroke": "transparent",
              "fill": "transparent",
              "icon": "/images/menu.png",
              "text": "系统管理",
              "url": "http://www.uieip.com/",
              "loc": "100 0",
              "size": "80 60"
            },
            {
              "category": "label",
              "key": "4shaWy6adnQ",
              "shape": "Rectangle",
              "stroke": "transparent",
              "fill": "transparent",
              "text": "我的系统管理",
              "fontSize": 16,
              "loc": "167 160",
              "size": "237 102"
            }
          ],
          "link": [
            {
              "from": "4shaWbVcoGO",
              "to": "4shaWy6adnQ",
              "text": "标记"
            }
          ]
        },
        isEdit: true,
        beforBindingTitle: function (titles, callback) {
          callback(titles);
        }, //标题绑定前
        afterBindingTitle: function () { },   //标题绑定后
        isEditChange: function (tf) {  //是否可用修改

        }
      };

      $.fn.yqDiagram.methods = {
        options: function (jq) {
          return options(jq);
        },
        resize: function (jq, res) {
          var opt = options(jq);
          resize(jq, res);
        },
        isEdit: function (jq, tf) {
          isEdit(jq, tf);
        },
        destroy: function (jq) {
          var $object = jq;
          destroy($object);
        },
        setValue: function (jq, val) {
          setValue(jq, val);
        },
        clear: function (jq) {
          setValue(jq, {});
        },
        getValue: function (jq) {
          return getValue(jq);
        },
      }

      function options($object) {
        return $object.data("dagram_opt");
      }

      function destroy($object) {
        var opt = options($object);

        $object.empty();

        $object.removeData("dagram_opt");
        $object.removeData("yqtype");
      }

      function resize($object, res) {
        var opt = options($object);
        var $uniqueID = $object.attr("ID");

        if (res === undefined) {
          res = {};
        }

        if (!(res.width === undefined)) {
          opt.width = res.width;
        }
        if (!(res.height === undefined)) {
          opt.height = res.height;
        }

        $object.css({
          "width": opt.width + "px",
          "height": opt.height + "px"
        });

        opt.$diagram_div.css({
          "width": opt.width + "px",
          "height": opt.height + "px"
        });
        opt.$diagram_div.find("canvas").css({
          "width": opt.width + "px",
          "height": opt.height + "px"
        });
        opt.$diagram_div.find("div").css({
          "width": opt.width + "px",
          "height": opt.height + "px"
        });
      }

      function isEdit($object, tf) {
        var opt = options($object);
        if (tf === undefined) {
          tf = opt.isEdit;
        } else {
          opt.isEdit = tf;
        }

        opt.diagram.clearSelection();
        opt.diagram.isReadOnly = !tf;

        // 启用用户交互，包括选择、拖拽等  
        opt.diagram.allowSelect = tf;
        //opt.diagram.allowDrag = tf;  
        opt.diagram.allowMultipleSelection = true; // 允许选择多个对象（默认为true）


        opt.diagram.contextMenu = diagramMenu($object);

        var nodes = opt.diagram.nodes;
        var it_node = nodes.iterator;
        while (it_node.next()) {
          var node = it_node.value;
          node.contextMenu = nodeMenu($object);
          if (node.category == "menu") {
            if (!tf) {
              node.cursor = "pointer";
              node.click = function (e, obj) {
                var data = obj.part.data;
                console.log("Double-clicked node data:", data);
                window.open(data.url);
              };
            } else {
              node.cursor = "auto";
              node.click = null;
            }
          }
        }

        var links = opt.diagram.links;
        var it = links.iterator;
        while (it.next()) {
          var link = it.value;
          link.contextMenu = linkMenu($object);
        }
      }

      function form_load($object) {
        var opt = options($object);
        resize($object);
        setValue($object);
        isEdit($object);
        opt.afterBindingTitle.call($object);
      }

      function init_menu($object) {
        var opt = options($object);
        var $uniqueID = $object.attr("ID");

        var diagram_div = $uniqueID + "_Div";

        var html_ = '<div id="' + diagram_div + '" style="overflow: ;flex-grow: 1; border: none; position: relative;">';
        html_ += '</div>';

        $object.css({
          "width": "100%",
          "display": "flex",
          "justify-content": "space-between",
          "background-color": "#FCFCFC"
        });

        opt.$diagram_div = $(html_);
        $object.append(opt.$diagram_div);

        $object.css({
          "width": opt.width + "px",
          "height": opt.height + "px"
        });
        opt.$diagram_div.css({
          "width": opt.width + "px",
          "height": opt.height + "px"
        });
        opt.$diagram_div.find("canvas").css({
          "width": opt.width + "px",
          "height": opt.height + "px",
        });
        opt.$diagram_div.find("div").css({
          "width": opt.width + "px",
          "height": opt.height + "px",
        });

        opt.$ = go.GraphObject.make;  // for conciseness in defining templates

        opt.diagram = new go.Diagram(diagram_div,
          {
            "contentAlignment": go.Spot.TopLeft,
            grid: opt.$(go.Panel, "Grid",  // a simple 10x10 grid
              opt.$(go.Shape, "LineH", { stroke: "lightgray", strokeWidth: 0.5 }),
              opt.$(go.Shape, "LineV", { stroke: "lightgray", strokeWidth: 0.5 })
            ),

            "LinkDrawn": showLinkLabel,
            "LinkRelinked": showLinkLabel,
            "undoManager.isEnabled": true,
          });

        nodeInit($object);
        linkInit($object);

        opt.diagram.toolManager.linkingTool.temporaryLink.routing = go.Link.Orthogonal;
        opt.diagram.toolManager.relinkingTool.temporaryLink.routing = go.Link.Orthogonal;

        form_load($object);
      }

      function diagramMenu($object) {
        var opt = options($object);
        var menu = null;
        if (opt.isEdit) {
          menu =
            opt.$("ContextMenu",
              makeButton($object, "添加菜单",
                function (e, obj) {
                  addMenuNode($object, e, obj);
                }),
              makeButtonLine($object),
              makeButton($object, "添加文本",
                function (e, obj) {
                  addLabelNode($object, e, obj);
                }),
            );
        }

        return menu;
      }

      function nodeMenu($object) {
        var opt = options($object);
        var menu = null;
        if (opt.isEdit) {
          var menu =
            opt.$("ContextMenu",
              makeButton($object, "添加菜单",
                function (e, obj) {
                  addMenuNode($object, e, obj);
                }),
              makeButton($object, "添加文本",
                function (e, obj) {
                  addLabelNode($object, e, obj);
                }),
              makeButtonLine($object),
              makeButton($object, "删除",
                function (e, obj) { e.diagram.commandHandler.deleteSelection() }),
              makeButton($object, "编辑",
                function (e, node) {
                  editNode($object, e, node);
                }),
            );
        }

        return menu;
      }

      function linkMenu($object) {
        var opt = options($object);
        var menu = null;
        if (opt.isEdit) {
          var menu =
            opt.$("ContextMenu",
              makeButton($object, "添加菜单",
                function (e, obj) {
                  addMenuNode($object, e, obj);
                }),
              makeButton($object, "添加文本",
                function (e, obj) {
                  addLabelNode($object, e, obj);
                }),
              makeButtonLine($object),
              makeButton($object, "编辑",
                function (e, obj) {

                }),
              makeButton($object, "删除",
                function (e, obj) { e.diagram.commandHandler.deleteSelection() }),
            );
        }
        return menu;
      }

      function makeButton($object, text, action, visiblePredicate) {
        var opt = options($object);
        return opt.$("ContextMenuButton",
          opt.$(go.TextBlock, text),
          {
            click: action,
            background: "lightgreen",
            margin: 0,
            width: 100,
            height: 33,

          },
          visiblePredicate ? new go.Binding("visible", "", (o, e) => o.diagram ? visiblePredicate(o, e) : false).ofObject() : {});
      }

      function makeButtonLine($object) {
        var opt = options($object);
        return opt.$(go.Shape, "Rectangle", {
          height: 1,
          stretch: go.GraphObject.Horizontal,
          stroke: "#C0C0C0",
          fill: "#C0C0C0"
        });
      }

      function nodeInit($object, callback) {
        var opt = options($object);

        var nodes = [{
          name: "menu",
          value:
            opt.$(go.Node, "Auto",
              {
                minSize: new go.Size(25, 40),
                resizable: true,
                resizeObjectName: "Shape",
              },
              new go.Binding("location", "loc", go.Point.parse),
              new go.Binding("desiredSize", "size", go.Size.parse),
              opt.$(go.Shape, "Rectangle",
                new go.Binding("stroke", "stroke"),
                new go.Binding("fill", "fill"),
              ),
              opt.$(go.Panel, "Vertical",
                {
                  defaultAlignment: go.Spot.Center,
                },
                opt.$(go.Picture,
                  {
                    margin: new go.Margin(0, 0, 3, 0),
                    width: 20,
                    height: 20,
                  },
                  new go.Binding("source", "icon")),
                opt.$(go.TextBlock,
                  {
                    margin: new go.Margin(3, 0, 0, 0),
                    alignment: go.Spot.Center,
                  },
                  new go.Binding("text", "text")),
              ),
              // four named ports, one on each side:
              makePort($object, "T", go.Spot.Top, go.Spot.TopSide, true, true),
              makePort($object, "L", go.Spot.Left, go.Spot.LeftSide, true, true),
              makePort($object, "R", go.Spot.Right, go.Spot.RightSide, true, true),
              makePort($object, "B", go.Spot.Bottom, go.Spot.BottomSide, true, true)
            )
        }, {
          name: "label",
          value: opt.$(go.Node, "Auto", {
            resizable: true,
            resizeObjectName: "Shape",
          },
            new go.Binding("location", "loc", go.Point.parse),
            new go.Binding("desiredSize", "size", go.Size.parse),
            opt.$(go.Shape,
              new go.Binding("figure", "shape"),  // 使用绑定来根据节点数据中的形状属性动态设置形状类型
              new go.Binding("stroke", "stroke"),
              new go.Binding("fill", "fill"),
            ),
            opt.$(go.TextBlock,
              {
                margin: 2,
                //editable: true,
                wrap: go.TextBlock.WrapFit
              },
              new go.Binding("text", "text"),
              new go.Binding("font", "fontSize")
            ),
            // four named ports, one on each side:
            makePort($object, "T", go.Spot.Top, go.Spot.Top, true, true),
            makePort($object, "L", go.Spot.Left, go.Spot.Left, true, true),
            makePort($object, "R", go.Spot.Right, go.Spot.Right, true, true),
            makePort($object, "B", go.Spot.Bottom, go.Spot.Bottom, true, true)
          )
        }];

        for (var i = 0; i < nodes.length; i++) {
          var node = nodes[i];
          opt.diagram.nodeTemplateMap.add(node.name, node.value);
        }

        if (typeof callback == "function") {
          callback();
        }
      }

      function linkInit($object) {
        var opt = options($object);
        opt.diagram.linkTemplate =
          opt.$(go.Link,  // the whole link panel
            {
              routing: go.Link.AvoidsNodes,
              curve: go.Link.JumpOver,
              corner: 5, toShortLength: 4,
              relinkableFrom: true,
              relinkableTo: true,
              reshapable: true,
              resegmentable: true,
              // mouse-overs subtly highlight links:
              mouseEnter: (e, link) => link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.2)",
              mouseLeave: (e, link) => link.findObject("HIGHLIGHT").stroke = "transparent",
              selectionAdorned: false,
            },
            new go.Binding("points").makeTwoWay(),
            opt.$(go.Shape,  // the highlight shape, normally transparent
              { isPanelMain: true, strokeWidth: 8, stroke: "transparent", name: "HIGHLIGHT" }),
            opt.$(go.Shape,  // the link path shape
              { isPanelMain: true, stroke: "gray", strokeWidth: 2 },
              new go.Binding("stroke", "isSelected", sel => sel ? "dodgerblue" : "gray").ofObject()),
            opt.$(go.Shape,  // the arrowhead
              { toArrow: "standard", strokeWidth: 0, fill: "gray" }),
            opt.$(go.Panel, "Auto",  // the link label, normally not visible
              { visible: false, name: "LABEL", segmentIndex: 2, segmentFraction: 0.5 },
              new go.Binding("visible", "visible").makeTwoWay(),
              opt.$(go.Shape, "RoundedRectangle",  // the label shape
                { fill: "#F8F8F8", strokeWidth: 0 }),
              opt.$(go.TextBlock, "Yes",  // the label
                {
                  textAlign: "center",
                  font: "10pt helvetica, arial, sans-serif",
                  stroke: "#333333",
                  editable: true
                },
                new go.Binding("text").makeTwoWay())
            )
          );
      }

      //节点连接端点的样式
      function makePort($object, name, align, spot, output, input) {
        var opt = options($object);

        var horizontal = align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom);
        // the port is basically just a transparent rectangle that stretches along the side of the node,
        // and becomes colored when the mouse passes over it
        return opt.$(go.Shape,
          {
            fill: "transparent",  // changed to a color in the mouseEnter event handler
            strokeWidth: 0,  // no stroke
            width: horizontal ? NaN : 4,  // if not stretching horizontally, just 8 wide
            height: !horizontal ? NaN : 4,  // if not stretching vertically, just 8 tall
            alignment: align,  // align the port on the main Shape
            stretch: (horizontal ? go.GraphObject.Horizontal : go.GraphObject.Vertical),
            portId: name,  // declare this object to be a "port"
            fromSpot: spot,  // declare where links may connect at this port
            fromLinkable: output,  // declare whether the user may draw links from here
            toSpot: spot,  // declare where links may connect at this port
            toLinkable: input,  // declare whether the user may draw links to here
            cursor: "pointer",  // show a different cursor to indicate potential link point
            mouseEnter: (e, port) => {  // the PORT argument will be this Shape
              if (!e.diagram.isReadOnly) port.fill = "rgba(255,0,255,0.5)";
            },
            mouseLeave: (e, port) => port.fill = "transparent"
          });
      }

      function addMenuNode($object, e, obj) {
        var opt = options($object);

        var nodedata = {
          "category": "menu",
          "key": $.getShortID(),
          "text": "系统管理",
          "icon": "/images/menu.png",
          "url": "http://www.uieip.com/",
          "loc": getAddLoc($object, e),
          "size": "65 50"
        };

        opt.diagram.model.addNodeData(nodedata);
        var newnode = opt.diagram.findNodeForData(nodedata);
        opt.diagram.select(newnode);
        newnode.contextMenu = nodeMenu($object);

        // 获取Shape对象  
        //var shape = newnode.findObject("Shape");
        //console.log("shape", shape);

        // if (shape !== null) {
        //   shape.figure = "RoundedRectangle";
        // }
      }

      function addLabelNode($object, e, obj) {
        var opt = options($object);

        var nodedata = {
          "category": "label",
          "shape": "Rectangle",
          "key": $.getShortID(),
          "text": "系统管理",
          "fontSize": 16,
          "loc": getAddLoc($object, e),
          "size": "101 52"
        };

        opt.diagram.model.addNodeData(nodedata);
        var newnode = opt.diagram.findNodeForData(nodedata);
        opt.diagram.select(newnode);
        newnode.contextMenu = nodeMenu($object);

        // 获取Shape对象  
        //var shape = newnode.findObject("Shape");
        // if (shape !== null) {
        //   shape.figure = "RoundedRectangle";
        // }
      }

      function getAddLoc($object, e) {
        var opt = options($object);
        var docPoint = e.documentPoint.copy().offset(-45, -40);
        var s_obj = opt.diagram.findPartAt(docPoint);
        if (s_obj) {
          if (s_obj instanceof go.Node) {
            docPoint = docPoint.offset(40, 0);
          } else if (s_obj instanceof go.Link) {
            docPoint = docPoint.offset(30, 0);
          }
        }
        return docPoint.x + " " + docPoint.y;
      }

      function editNode($object, e, node) {
        var opt = options($object);
        var data = node.part.data;
        if (data.category == "menu") {
          opt.diagram.model.setDataProperty(data, "text", "dddddddddddd");
        }
        console.log(e, node, data);
      }

      function nodeStyle($object) {
        return [
          new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
          {
            locationSpot: go.Spot.Center,
          }
        ];
      }

      function textStyle() {
        return {
          font: "bold 11pt Lato, Helvetica, Arial, sans-serif",
          stroke: "#F8F8F8"
        }
      }

      function showLinkLabel(e) {
        var label = e.subject.findObject("LABEL");
        if (label !== null) label.visible = (e.subject.fromNode.data.category === "Conditional");
      }

      function setValue($object, val) {
        var opt = options($object);
        if (!(val === undefined)) {
          if (typeof val == "string") {
            val = JSON.parse(val);
          }
          opt.value = val;
        }

        var loadedData = {
          "class": 'GraphLinksModel',
          "nodeDataArray": opt.value.node,
          "linkDataArray": opt.value.link
        };
        opt.diagram.model = go.Model.fromJson(loadedData);

        loadedData.nodeDataArray.forEach(function (data) {
          var node = opt.diagram.findNodeForData(data);
          if (node) {
            // 设置节点位置  
            var loc = go.Point.parse(data.loc);
            node.location = loc;

            // 设置节点尺寸（如果需要的话，注意这可能会覆盖布局算法的结果）  
            var bounds = go.Rect.parse(data.bounds);
            node.actualBounds = bounds;
          }
        });
      }

      function getValue($object) {
        var opt = options($object);

        var json_ = opt.diagram.model.toJson();
        json_ = typeof json_ == "string" ? JSON.parse(json_) : json_;

        for (var i = 0; i < json_.nodeDataArray.length; i++) {
          var node = opt.diagram.findNodeForKey(json_.nodeDataArray[i].key);
          var loc = node.location;
          json_.nodeDataArray[i].loc = loc.x + " " + loc.y;
          var bounds = node.actualBounds;
          json_.nodeDataArray[i].size = bounds.width + " " + bounds.height;
        }

        for (var i = 0; i < json_.linkDataArray.length; i++) {
          delete json_.linkDataArray[i].points
        }

        return {
          "node": json_.nodeDataArray,
          "link": json_.linkDataArray
        };
      }

      //流程打印
      function printDiagram($object) {
        var opt = options($object);
        var svgWindow = window.open();
        if (!svgWindow) return;  // failure to open a new Window
        var printSize = new go.Size(700, 960);
        var bnds = opt.myDiagram.documentBounds;
        var x = bnds.x;
        var y = bnds.y;
        while (y < bnds.bottom) {
          while (x < bnds.right) {
            var svg = opt.myDiagram.makeSvg({ scale: 1.0, position: new go.Point(x, y), size: printSize });
            svgWindow.document.body.appendChild(svg);
            x += printSize.width;
          }
          x = bnds.x;
          y += printSize.height;
        }

        setTimeout(function () {
          svgWindow.print()
          svgWindow.close();
        }, 1);
      }
    })(jQuery);
  </script>
</body>

</html>